#include "scriptFile.h"
#include <QDir>
#include <QRegExp>
#include <QFile>
#include <QTextStream>
#include <stdexcept>

namespace SDHuaYi_Script
{

    ScriptFile::ScriptFile()
    {
    }

    enum_KEYWORD ScriptFile::getKeyWord(QString keyWord)
    {
        enum_KEYWORD ret;

        if (keyWord == "define")
            ret = KEYWORD_define;
        else if (keyWord == "integer")
            ret = KEYWORD_integer;
        else if (keyWord == "string")
            ret = KEYWORD_string;
        else if (keyWord == "if")
            ret = KEYWORD_if;
        else if (keyWord == "call")
            ret = KEYWORD_call;
        else if (keyWord == "goto")
            ret = KEYWORD_goto;
        else if (keyWord == "ret")
            ret = KEYWORD_ret;
        else if (keyWord == "pause")
            ret = KEYWORD_pause;
        else if (keyWord == "exit")
            ret = KEYWORD_exit;
        else if (keyWord == "f_flagerr")
            ret = KEYWORD_f_flagerr;
        else if (keyWord == "insert")
            ret = KEYWORD_insert;
        else
            ret = KEYWORD_other;

        return ret;
    }

    QString ScriptFile::GetFileName(QString currentDir, QString fileName)
    {
        while (1)
        {
            if (fileName.startsWith("../"))
            {
                currentDir = currentDir.mid(0, currentDir.lastIndexOf('\\'));
                fileName = fileName.mid(fileName.indexOf('/') + 1);
                continue;
            }
            if (fileName.startsWith("..\\"))
            {
                currentDir = currentDir.mid(0, currentDir.lastIndexOf('\\'));
                fileName = fileName.mid(fileName.indexOf('\\') + 1);
                continue;
            }
            if (fileName.startsWith("./"))
            {
                fileName = fileName.mid(fileName.indexOf('/') + 1);
            }
            else
            {
                break;
            }
        }
        return currentDir + "\\" + fileName.replace("/", "\\");
    }

    command ScriptFile::getCommand(QString scmd, qint32 lineNum, qint32 startIndex)
    {
        command newCmd{scmd, startIndex, CMD_EMPTY};
        if (scmd == NULL)
        {
            return newCmd;
        }
        // 替换tab为空格
        QRegExp tabRegex("\\t");
        scmd.replace(tabRegex, " ");

        // 添加空格到命令行
        command tcmd;
        tcmd.Format(scmd);
        // 将多空格变为1个
        QRegExp multiSpaceRegex("\\s+");
        scmd.replace(multiSpaceRegex, " ");
        // 将多；号变为1个
        QRegExp multiSemicolonRegex(";+");
        scmd.replace(multiSemicolonRegex, ";");

        if (scmd.contains(";"))
        {
            QStringList scmdParas = scmd.split(";");
            for (int i = 0; i < scmdParas.count(); i++)
            {
                // 去掉空格
                scmdParas[i] = scmdParas[i].trimmed();
            }

            qint32 index = scmd.indexOf(';');
            if (index != -1)
                newCmd.Comment = scmd.mid(index);
            else
                newCmd.Comment = QString();

            // 检查是否judge
            if (scmdParas[1].replace(" ", "").count() > QString("judge:sw=").count())
            {
                QString strJudge = scmdParas[1].replace(" ", "");
                if (strJudge.toLower().startsWith("judge:sw="))
                {
                    // 包含judge
                    // 取 = 后面 去掉空格
                    newCmd.ExptRe = strJudge.mid(strJudge.indexOf('=') + 1);
                    if (newCmd.ExptRe.count() < 4)
                    {
                        throw std::runtime_error("judge格式错误");
                    }
                }
            }

            // 有注释去掉注释
            scmd = scmd.mid(0, scmd.indexOf(';')).trimmed();
        }

        if (scmd.isEmpty())
        {
            // 纯注释行
            newCmd.Type = CMD_COMMENT;
            newCmd.Value.clear();
            return newCmd;
        }

        QStringList cmdParas = scmd.trimmed().split(' ', QString::SkipEmptyParts);
        QString cmdPara = cmdParas[0].toLower();

        switch (getKeyWord(cmdPara))
        {
        case KEYWORD_define:
        case KEYWORD_integer:
        case KEYWORD_string:
            // 基本关键字
            if (!scmd.contains(" "))
            {
                // 只有关键字
                newCmd.Type = CMD_EMPTY;
            }
            else
            {
                newCmd.Type = CMD_DEFINE;
            }
            break;
        case KEYWORD_if:
        case KEYWORD_call:
        case KEYWORD_goto:
        case KEYWORD_ret:
        case KEYWORD_pause:
        case KEYWORD_exit:
        case KEYWORD_f_flagerr:
            // 控制关键字
            newCmd.Type = CMD_FLOW;
            break;
        case KEYWORD_insert:
            newCmd.Type = CMD_INSERT;
            break;
        default:
            // 检查是否是标号
            if (cmdParas[0].endsWith(":"))
            {
                ScriptLabel label{lineNum, cmdParas[0].mid(0, cmdParas[0].indexOf(':'))};
                Labels.append(label);
                newCmd.Type = CMD_LABEL;
            }
            else if ((scmd.contains("?")) || scmd.contains("="))
            {
                // 显示或赋值语句
                newCmd.Type = CMD_DATAOP;
            }
            else
            {
                // 可执行命令
                newCmd.Type = CMD_APDU;
            }
            break;
        }
        // 重新赋值解析后的cmd
        newCmd.Value = scmd;

        // 添加 cmd list
        return newCmd;
    }

    // 展开include
    void ScriptFile::spreadInclude(QString scmd)
    {
        // 处理include
        QStringList cmdParas = scmd.split(' ', QString::SkipEmptyParts);
        // 如果有包含文件 展开新文件
        QString fileName = cmdParas[1].trimmed();
        if (fileName.contains(":") || fileName.contains(".."))
        {
            // 相对路径
            fileName = GetFileName(this->Name.mid(0, this->Name.lastIndexOf('\\')), fileName);
        }

        ScriptFile scriptFileNew{this->ScriptCmds, this->Labels};
        scriptFileNew.ScriptSpread(fileName);

         // 添加到this里
        ScriptCmds.append(scriptFileNew.ScriptCmds);
    }

    // 脚本展开
    void ScriptFile::ScriptSpread(QString script, QString path)
    {
        // 拆分命令
        QStringList CMDS = script.split("\n", QString::SkipEmptyParts);

        Name = path;

        for (int i = 0; i < CMDS.count(); i++)
        {
            try
            {
                QString scmd = CMDS[i].trimmed();
                if (scmd.contains(" ") && scmd.mid(0, scmd.indexOf(" ")).toLower().startsWith("include"))
                {
                    spreadInclude(scmd);
                }
                else
                {
                    // 添加命令
                    command newCmd = getCommand(scmd, ScriptCmds.count(), cmdStartIndex);
                    newCmd.cmdStr = CMDS[i];
                    ScriptCmds.append(command(newCmd));
                    cmdStartIndex += newCmd.cmdStr.count() + QString("\n").count();
                }
            }
            catch (...)
            {
                throw std::runtime_error(QString("脚本格式错误，行号：%1").arg(i).toStdString());
            }
        }
    }

    void ScriptFile::ScriptSpread(QString ScriptFileName)
    {
        Name = ScriptFileName;
        // 获取格式
        QFile file(Name);
        if (!file.open((QIODevice::ReadOnly) | QIODevice::Text))
            return;

        QTextStream in(&file);

        while (!in.atEnd())
        {
            // 读一行
            QString scmd = in.readLine();
            scmd = scmd.trimmed();

            if (scmd.contains(" ") && scmd.mid(0, scmd.indexOf(" ")).toLower().startsWith("include"))
            {
                spreadInclude(scmd);
            }
            else
            {
                // 添加命令
                command newCmd = getCommand(scmd, ScriptCmds.count());
                newCmd.cmdStr = scmd;
                ScriptCmds.append(newCmd);
            }
        }

        file.close();
    }

} /* endof namespace SDHuaYi_Script */
